#pragma once

#include <iostream>
#include <string>
#include <vector>
#include "Common.hpp"
#include "log.hpp"
#include <sstream>
#include <unordered_map>
#include <fstream>

using namespace LogModule;

const std::string Sep = "\r\n";
const std::string Blankline = "\r\n";           // 空行其实就是一个\r\n
const std::string HeaderLineSep = ": ";         // 报头的格式分离符号
const std::string defaulthomepage = "wwwroot";  // web根目录
const std::string http_version = "HTTP/1.0";    // 默认版本
const std::string LineSep = " ";                // 行内分隔符
const std::string page404 = "wwwroot/404.html"; // 404页面
const std::string firstpage = "index.html";     // 首页

class HttpRequest
{
public:
    bool ParseHeaderkv() // 我们的报头数据都在类成员变量数组中，所以不需要传递参数了
    // 我们只需要对这个数组进行处理就行了
    {
        std::string key, value;
        for (auto &header : _req_header)
        {
            // 写到这里我们会发现，header提取出来的还是一个key:value的字符串，我们没有进行分离，所以还需要先分离字符串
            if (SplitString(header, HeaderLineSep, &key, &value))
            {
                _headerkv.insert(std::make_pair(key, value));
            }
        }
        return true;
    }

    bool IsHasArgs()
    {
        return _isexec;
    }

    bool ParseHeader(std::string &req_ptr)
    { 
        std::string line;

        while (true)
        {
            bool ret = ParseOneLine(req_ptr, &line, Sep); // 只会提取一行报头数据

            // 我们之前说过，该函数接口调用返回的情况有三种:
            //  1、传过来的是正常字符串，我们进行正常的处理。
            //  2、传过来的只有请求行(或者此时只有一个空行\r\n)，其他地方为空串，但是返回值为真。
            //  3、传过来的请求行不完全，且out输出为空串，返回值为假。

            if (ret && !line.empty()) // 第一种情况
            {
                _req_header.push_back(line); // 正常读取，把请求报头存储在vector中
            }
            else if (ret && line.empty()) // 第二种情况
            {
                // 在我们检测请求报头时出现空行，就说明我们的请求报头已经结束了，这个就是我们的空行
                _blankline = Sep;
                break; // 我们直接break，结束处理逻辑
            }
            else
            {
                // 其他情况都是不正常的，我们直接返回false
                return false;
            }
        }

        // 这里还缺少一个步骤
        ParseHeaderkv(); // 分离key:value

        return true;
    }

    bool Deserialize(std::string &req_str)
    {
        if (ParseOneLine(req_str, &_req_line, Sep))
        {
            ParseReqLine(_req_line); // 对请求行进行处理
            // 其他处理.....
            ParseHeader(req_str); // 对请求报头进行处理

            // 剩下的就是请求正文：
            _body = req_str;

            if (_method == "POST")
            {
                _isexec = true;
                // POST方法，我们的参数都在body中
                _args = _body;
                _path = _uri;
                LOG(LogLevel::DEBUG) << "_path:" << _path;
                LOG(LogLevel::DEBUG) << "_args:" << _args;
            }
            else if (_method == "GET") // GET
            {
                // 我们要进行分离，通过?
                auto pos = _uri.find("?");
                if (pos != std::string::npos)
                {
                    _isexec = true;
                    // 说明参数在Uri上，分离：82.157.70.111:8080/login.html?username=zhangsan&pwd=654321
                    _path = _uri.substr(0, pos);
                    _args = _uri.substr(pos + 1);
                    LOG(LogLevel::DEBUG) << "_path:" << _path;
                    LOG(LogLevel::DEBUG) << "_args:" << _args;
                }
            }
        }
        return true;
    }

    std::string GetContent(const std::string path)
    {
        std::string content;                      // 创建一个空字符串，用于存储文件内容
        std::ifstream in(path, std::ios::binary); // 以二进制模式打开文件,std::ios::binary是二进制模式标志，防止文本转换

        if (!in.is_open())
            return std::string();

        // 将文件指针移动到文件末尾
        in.seekg(0, in.end);
        // 获取当前文件指针位置（即文件大小）
        int filesize = in.tellg();
        // 随后又将文件指针移动到开头，以上步骤是为了获取大小预定空间
        in.seekg(0, in.beg);

        // 预分配足够的内存空间
        content.resize(filesize);
        // 一次性读取整个文件内容到字符串中
        in.read((char *)content.c_str(), filesize);
        in.close();

        LOG(LogLevel::DEBUG) << "content length: " << content.size();
        return content;
    }

    std::string Uri()
    {
        return _uri;
    }

    std::string Path()
    {
        return _path;
    }

    std::string Args()
    {
        return _args;
    }

    std::string Suffix() // 我们这个函数只获取了文件的拓展名，并未获取type，所以要查表进行转换
    {
        //   wwwroot/index.html   wwwroot/images/111.jpg  我们要返回申请资源的类型
        auto pos = _uri.rfind("."); // 我们怎么知道数据的类型的呢？答：通过后缀
        if (pos == std::string::npos)
        {
            return std::string(".html");
        }
        else
        {
            return _uri.substr(pos);
        }
    }

private:
    // 不想让外部调用这个函数，放到private中
    void ParseReqLine(std::string &req)
    {
        std::stringstream ss(req);
        ss >> _method >> _uri >> _version;
    }

private:
    std::string _req_line;                // 请求行
    std::vector<std::string> _req_header; // 请求报头
    std::string _blankline;               // 空行
    std::string _body;                    // 请求正文

    // 在反序列化的过程中，细化我们解析出来的字段

    std::string _method;  // 请求方法
    std::string _uri;     // 请求路径
    std::string _version; // http版本

    // 当url格式为http://82.157.70.111:8080/login.html?username=zhangsan&pwd=654321#时，将其划分为_path与_args
    std::string _args; //?username=zhangsan&pwd=654321  或者用来存储body里的参数
    std::string _path; // 82.157.70.111:8080/login.html

    std::unordered_map<std::string, std::string> _headerkv; // 存储请求报头数据

    bool _isexec = false; // 是否需要交互，如果为真我们就走交互的那个处理方案而不是申请静态资源的方案
};

class HttpResponse
{
public:
    HttpResponse()
        : _version(http_version),
          _blank_line(Sep)
    {
    }

    void Serialize(std::string *resp_str)
    {
        for (auto &header : _header_kv)
        {
            _resp_header.push_back(header.first + HeaderLineSep + header.second);
        }
        _resp_line = _version + LineSep + std::to_string(_status_code) + LineSep + _status_desc + Sep;

        // 序列化
        *resp_str = _resp_line;
        for (auto &line : _resp_header)
        {
            *resp_str += (line + Sep);
        }
        *resp_str += _blank_line;
        *resp_str += _body;
    }

    void SetHeader(const std::string &k, const std::string &v)
    {
        _header_kv[k] = v;
    }

    void SetBody(const std::string&body)
    {
        _body=body;
    }

    void Build(HttpRequest &req_str)
    {
        // 先获取传过来的想要获取的资源的URL
        std::string URL = defaulthomepage + "/" + req_str.Uri();
        // 但注意，此时可能有坑，因为传过来的可能以/结尾，那么结合的URL为：
        //: wwwroot/
        // 此时这个URL无法指明具体的想要获取的资源，所以我们要先进行判断：
        if (URL.back() == '/')
        {
            // 那么我们就默认这个路径下有一个index.html，即该路径的首页（这个板块的首页）
            URL += firstpage;
        }

        // 尝试获取网页内容
        _content = req_str.GetContent(URL);
        if (_content.empty())
        {
            // 用户请求的资源并不存在
            _status_code = 404;
            _content = req_str.GetContent(page404);
        }
        else
        {
            _status_code = 200;
        }

        // 根据状态码甚至文本信息
        _status_desc = Code2Desc(_status_code);

        if (!_content.empty())
        {
            SetHeader("Content-Length", std::to_string(_content.size()));
        }

        // 获取文件拓展名并转化为content-type
        std::string mime_type = Suffix2Desc(req_str.Suffix());
        // 设置报头信息
        SetHeader("Content-Type", mime_type);

        _body = _content;
    }
    void SetCode(int code)
    {
        _status_code=code;
    }
private:
    std::string Code2Desc(int status_code)
    {
        switch (status_code)
        {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        case 301:
            return "Moved Permanently";
        case 302:
            return "Found";
        default:
            return std::string();
        }
    }

    std::string Suffix2Desc(const std::string &suffix)
    {
        // 使得文件拓展名字符串转化为对应的content-type
        if (suffix == ".html")
        {
            return "text/html";
        }
        else if (suffix == ".jpg")
        {
            return "application/x-jpg";
        }
        else
        {
            return "text/html";
        }
    }

private:
    // 最终要这4部分，构建应答
    std::string _resp_line;                // 响应行
    std::vector<std::string> _resp_header; // 响应报头
    std::string _blank_line;               // 空行
    std::string _body;                     // 正文

    // 组成响应行的变量：
    std::string _version;     // 版本
    int _status_code;         // 状态码
    std::string _status_desc; // 原因

    std::string _content; // 响应正文，我们从目标URL中提取的数据

    // 响应报头的kv值
    std::unordered_map<std::string, std::string> _header_kv;
};